package com.zenika.zenbinding.client.util;

import com.google.gwt.core.client.GWT;
import com.zenika.zenbinding.client.converter.Converter;
import com.zenika.zenbinding.client.converter.Converters;
import com.zenika.zenbinding.client.exception.ConverterException;
import com.zenika.zenbinding.client.exception.DataException;
import com.zenika.zenbinding.client.model.BusinessObject;

/**
 * This utility class allows to :
 * <ul>
 * <li>Get and set a property of an object with a pointed annotation (like "address.streetName").</li>
 * <li>Get the type name of an object</li>
 * <li>Get a converter matching to a property</li>
 * </ul>
 * 
 * @author Zenika
 */
public class BeanUtil {
  
  private static WrapperLoader loader;
  static {
    loader = (WrapperLoader) GWT.create(WrapperLoader.class);
  }
  
  /**
   * Wrap an object and return it once wrapped. A wrapper permits to set and get properties of an object by their name.
   * 
   * @param object
   *          The object to wrap.
   * @return The wrapper containing the object given in parameter.
   */
  public static Wrapper getWrapper(BusinessObject object) {
    String className = object.getClass().getName();
    Wrapper wrapper = loader.get(className);
    wrapper.setContent(object);
    return wrapper;
  }
  
  public static Object getProperty(BusinessObject source, String propertyPath) throws DataException {
    String[] objects = decompPath(propertyPath);
    Wrapper wrapper = getLastWrapper(objects, source);
    String propertyName = objects[objects.length - 1];
    Object result = null;
    result = wrapper.getProperty(propertyName);
    return result;
    
  }
  
  public static boolean isPropertyNull(BusinessObject source, String propertyPath) throws DataException {
    String[] objects = decompPath(propertyPath);
    Wrapper wrapper = getLastWrapper(objects, source);
    String propertyName = objects[objects.length - 1];
    // Object result = null;
    // result = wrapper.isPropertyNull(propertyName);
    return wrapper.isPropertyNull(propertyName);
  }
  
  public static boolean isPropertyExist(BusinessObject source, String propertyPath) {
    String[] objects = decompPath(propertyPath);
    Wrapper wrapper = getLastWrapper(objects, source);
    String propertyName = objects[objects.length - 1];
    return wrapper.isPropertyExist(propertyName);
  }
  
  public static void setProperty(BusinessObject source, String propertyPath, Object target) {
    String[] objects = decompPath(propertyPath);
    Wrapper wrapper = getLastWrapper(objects, source);
    String propertyName = objects[objects.length - 1];
    try {
      wrapper.setProperty(propertyName, target);
    }
    catch ( DataException e ) {
      e.printStackTrace();
    }
    
  }
  
  private static String[] decompPath(String propertyPath) {
    String[] objects = propertyPath.split("\\.");
    return objects;
  }
  
  private static Wrapper getLastWrapper(String[] objects, BusinessObject source) {
    Wrapper wrapper = getWrapper(source);
    for ( int i = 0; i < objects.length - 1; i++ ) {
      String propertyName = objects[i];
      Object property = null;
      try {
        property = wrapper.getProperty(propertyName);
      }
      catch ( DataException e ) {
        e.printStackTrace();
      }
      wrapper = getWrapper((BusinessObject) property);
    }
    return wrapper;
  }
  
  public static String getTypeName(BusinessObject object, String propertyPath) {
    Object obj = null;
    if ( propertyPath != null ) {
      try {
        obj = getProperty(object, propertyPath);
      }
      catch ( DataException e ) {
        e.printStackTrace();
      }
    } else {
      obj = object;
    }
    String className = obj.getClass().getName();
    return className;
  }
  
  public static Converters converters;
  static {
    converters = Converters.getInstance();
  }
  
  /**
   * Returns the Converter matching with the type of a property.
   * 
   * @param object
   *          The object containing the property
   * @param propertyPath
   *          The property path;
   * @return The corresponding converter
   * @throws DataException
   */
  public static Converter getDefaultConverter(BusinessObject object, String propertyPath) throws DataException {
    String type = getTypeName(object, propertyPath);
    return findConverter(type);
  }
  
  /**
   * Return the converter corresponding to an object.
   * 
   * @param object
   * @return The good converter
   * @throws DataException
   */
  public static Converter getDefaultConverter(Object object) throws DataException {
    String type = object.getClass().getName();
    return findConverter(type);
  }
  
  private static Converter findConverter(String type) throws DataException {
    if ( type.equals("java.lang.String") ) {
      return converters.STRING;
    } else
      if ( type.equals("java.lang.Integer") ) {
        return converters.INTEGER;
      } else
        if ( type.equals("java.lang.Boolean") ) {
          return converters.BOOLEAN;
        } else
          if ( type.equals("java.lang.Double") ) {
            return converters.DOUBLE;
          } else
            if ( type.equals("java.lang.Float") ) {
              return converters.FLOAT;
            } else
              if ( type.equals("java.lang.Short") ) {
                return converters.SHORT;
              } else
                if ( type.equals("java.lang.Long") ) {
                  return converters.LONG;
                } else
                  if ( type.equals("java.lang.Byte") ) {
                    return converters.BYTE;
                  } else
                    if ( type.equals("java.lang.Character") ) {
                      return converters.CHARACTER;
                    } else
                      if ( type.equals("java.util.Date") ) {
                        return converters.DATE;
                      } else {
                        throw new ConverterException("No converter found. Was your property a primitive or a primitive wrapper?");
                      }
  }
  
  public static BusinessObject clone(BusinessObject object) {
    Wrapper wrapper = getWrapper(object);
    return (BusinessObject) wrapper.clone();
  }
  
}
